local sct_CRIT_FADEINTIME = 0.3;
local sct_CRIT_HOLDTIME = 2.0;
local sct_CRIT_FADEOUTTIME = 0.5;
local sct_CRIT_X_OFFSET = 100;
local sct_CRIT_Y_OFFSET = 75;
local sct_CRIT_SIZE_PERCENT = 1.25;
local sct_CRIT_FLASH_SIZE_PERCENT = 2;
local sct_MAX_SPEED = .025;
local sct_MIN_UPDATE_SPEED = .01;
local sct_SIDE_POINT = 210;
local sct_MAX_DISTANCE = 150;
local sct_DIRECTION = 1;
local sct_SPRINKLER_START = 18
local sct_SPRINKLER_STEP = -3;
local sct_SPRINKLER_RADIUS = 20;
local sct_SPRINKLER = sct_SPRINKLER_START;

--Animation System variables
local sct_TEXTCOUNT = 30                -- Number of text that can animate
local arrAniData = {};                  -- table to hold texts to animate
local arrAniData2 = {};
SCT.ArrayAniData = {arrAniData, arrAniData2};

----------------------
--Display the Text
function SCT:DisplayText(msg, color, iscrit, type, frame, anitype)
    local adat, curDir;
    local startpos, lastpos, textsize;

    --Set up  text animation
    adat = self:GetNextAniObj(frame);

    --set override animation
    adat.anitype = anitype or adat.anitype;

    --If its a crit hit, increase the size
    if (iscrit) then
        adat.textsize = adat.textsize * sct_CRIT_SIZE_PERCENT;
        if (self.db.profile["STICKYCRIT"]) then
            adat.crit = true;
            local critcount = self:CritCount(adat.frame);
            adat.posY = (adat.toppoint + adat.posY)/2;
            --if there are other Crits active, set offset.
            if (critcount > 0) then
                local randomposx = random(-1,1);
                local randomposy = random(-1,1);
                if randomposx == 0 and randomposy == 0 then randomposy = 1 end;
                adat.posX = adat.posX + (randomposx*sct_CRIT_X_OFFSET);
                adat.posY = adat.posY + (randomposy*sct_CRIT_Y_OFFSET);
            end
            --if flash crits are on
            if (self.db.profile["FLASHCRIT"]) then
                adat.critsize = adat.textsize * sct_CRIT_FLASH_SIZE_PERCENT;
                if (adat.textsize > 32) then adat.textsize = 32 end;
            end
        end
    end

    --if its not a sticky critm set up normal text start position
    if (adat.crit ~= true) then
        if (adat.anitype == 1) then
            --get the last known point of active items
            lastpos = self:MinPoint(adat.frame)
            if (not adat.direction) then
                --move the position down
                if ((lastpos - adat.posY) <= adat.textsize) then
                    adat.posY = adat.posY - (adat.textsize - (lastpos - adat.posY));
                end
                --if its gone too far down, stop
                if (adat.posY < (adat.bottompoint - sct_MAX_DISTANCE)) then
                    adat.posY = (adat.bottompoint - sct_MAX_DISTANCE)
                    adat.posX = adat.posX + (random(-1,1)*sct_CRIT_X_OFFSET)
                end
                adat.addY = self.db.profile["MOVEMENT"];
            else
                adat.posY = adat.toppoint
                --move the position up
                if ((adat.posY - lastpos) <= adat.textsize) then
                    adat.posY = adat.posY + (adat.textsize - (adat.posY - lastpos));
                end
                --if its gone too far up, stop
                if (adat.posY > (adat.toppoint + sct_MAX_DISTANCE)) then
                    adat.posY = (adat.toppoint + sct_MAX_DISTANCE)
                    adat.posX = adat.posX + (random(-1,1)*sct_CRIT_X_OFFSET)
                end
                adat.addY = -1*self.db.profile["MOVEMENT"];
            end
        else
            --get direction type
            if (adat.sidedir == 1) then
                sct_DIRECTION = sct_DIRECTION * -1;
                curDir = sct_DIRECTION;
            elseif (adat.sidedir == 2) then
                if (type=="event") then curDir = -1 else curDir = 1 end
            elseif (adat.sidedir == 3) then
                if (type=="event") then curDir = 1 else curDir = -1 end
            elseif (adat.sidedir == 4) then
                curDir = 1;
            elseif (adat.sidedir == 5) then
                curDir = -1;
            end
            adat.sidedir = curDir;
            --set animation start pos.
            if (adat.anitype == 2) then
                adat.addY = random(3,6);
                adat.posX = adat.posX - (20 * adat.sidedir);
            elseif (adat.anitype == 3) then
                adat.posX = adat.posX - (55 * adat.sidedir);
                adat.posY = adat.bottompoint + (random(0,200) - 100);
                adat.addX = self.db.profile["MOVEMENT"];
            elseif (adat.anitype == 4) then
                adat.posX = adat.posX - (20 * adat.sidedir);
                adat.addY = random(8,13);
                adat.addX = random(8,13);
            elseif (adat.anitype == 5) then
                adat.posX = adat.posX - (20 * adat.sidedir);
                adat.addY = random(10,15);
                adat.addX = random(10,15);
            elseif (adat.anitype == 6) then
                adat.addX = sct_SPRINKLER;
                adat.addY = math.sqrt((sct_SPRINKLER_RADIUS ^ 2) - math.abs((sct_SPRINKLER ^ 2)))
                if ( adat.direction) then
                    adat.addY = adat.addY * -1;
                end
                sct_SPRINKLER = sct_SPRINKLER + sct_SPRINKLER_STEP;
                if (sct_SPRINKLER < (sct_SPRINKLER_START * -1)) then
                    sct_SPRINKLER = sct_SPRINKLER_START;
                end
            end
        end
    end

    --set default color if none
    if (not color) then color = {r = 1.0, g = 1.0, b = 1.0} end
    --If they want to tag all self events
    if (self.db.profile["SHOWSELF"]) then
        msg = SCT.LOCALS.SelfFlag..msg..SCT.LOCALS.SelfFlag
    end

    --set up text
    self:SetFontSize(adat, adat.font, adat.textsize, adat.fontshadow);
    adat:SetTextColor(color.r, color.g, color.b);
    adat:SetAlpha(adat.alpha);
    adat:SetPoint("CENTER", "UIParent", "CENTER", adat.posX, adat.posY);
    adat:SetText(msg);
    adat:Show();
    tinsert(self.ArrayAniData[adat.frame], adat);
    --Start up onUpdate
    if (not SCT_ANIMATION_FRAME:IsVisible()) then
        SCT_ANIMATION_FRAME:Show();
    end
end

----------------------
-- Upate animations that are being used
function SCT:UpdateAnimation(elapsed)
    local anyActive = false;
    local i, key, value;
    for i = 1, table.getn(self.ArrayAniData) do
        for key, value in pairs(self.ArrayAniData[i]) do
            if (value:IsShown()) then
                anyActive = true;
                self:DoAnimation(value, elapsed);
            end
        end
    end
    --if none are active, stop onUpdate;
    if ((anyActive ~= true) and (SCT_ANIMATION_FRAME:IsVisible())) then
        SCT_ANIMATION_FRAME:Hide();
    end
end

----------------------
--Move text to get the animation
function SCT:DoAnimation(aniData, elapsed)
    local speed = self.db.profile["ANIMATIONSPEED"] / 1000;
    --If a crit
    aniData.lastupdate = aniData.lastupdate + elapsed;
    if (aniData.crit) then
        self:CritAnimation(aniData,speed);
    --else normal text or event text
    else
        --if its time to update, move the text step positions
        while (aniData.lastupdate > speed) do
            --calculate animation
            if (aniData.anitype == 1) then
                self:VerticalAnimation(aniData);
            elseif (aniData.anitype == 2) then
                self:RainbowAnimation(aniData);
            elseif (aniData.anitype == 3) then
                self:HorizontalAnimation(aniData);
            elseif (aniData.anitype == 4) then
                self:AngledDownAnimation(aniData);
            elseif (aniData.anitype == 5) then
                self:AngledUpAnimation(aniData);
            elseif (aniData.anitype == 6) then
                self:SprinklerAnimation(aniData);
            end
            --set update on FPS mode
            if (self.db.profile["FPSMODE"]) then
                aniData.lastupdate = aniData.lastupdate - speed;
            else
                aniData.lastupdate = 0;
            end
            --move text
            aniData:SetAlpha(aniData.alpha);
            aniData:SetPoint("CENTER", "UIParent", "CENTER", aniData.posX, aniData.posY);
            --reset when alpha is 0
            if (aniData.alpha <= 0) then
                self:AniReset(aniData);
            end
        end
    end
end

----------------------
--Do Crit Animation
function SCT:CritAnimation(aniData,speed)
    local elapsedTime = aniData.lastupdate;
    local fadeInTime = sct_CRIT_FADEINTIME;
    if ( elapsedTime < fadeInTime ) then
        local alpha = (elapsedTime / fadeInTime);
        alpha = alpha * aniData.alpha;
        aniData:SetAlpha(alpha);
        --if flash crits are on
        if (aniData.critsize) then
            local critsize = floor(aniData.critsize - ((aniData.critsize - aniData.textsize)*(elapsedTime/sct_CRIT_FADEINTIME)));
            aniData:SetTextHeight(critsize);
        end;
        return;
    end
    --if flash crits are on, reset size to make sure its clean for display
    if (aniData.critsize) then
        aniData:SetTextHeight(aniData.textsize);
        aniData.critsize = nil;
    end
    local holdTime = (sct_CRIT_HOLDTIME * (speed/sct_MAX_SPEED));
    if ( elapsedTime < (fadeInTime + holdTime) ) then
        aniData:SetAlpha(aniData.alpha);
        return;
    end
    local fadeOutTime = sct_CRIT_FADEOUTTIME;
    if ( elapsedTime < (fadeInTime + holdTime + fadeOutTime) ) then
        local alpha = 1 - ((elapsedTime - holdTime - fadeInTime) / fadeOutTime);
        alpha = alpha * aniData.alpha;
        aniData:SetAlpha(alpha);
        return;
    end
    --reset crit
    self:AniReset(aniData);
end

----------------------
--Do Vertical Animation
function SCT:VerticalAnimation(aniData)
    local step = math.abs(aniData.addY);
    local alphastep = 0.01 * step;
    local max = sct_MAX_DISTANCE*.66
    aniData.delay = aniData.delay + 1;
    if (aniData.delay > (max/step)) then
      aniData.alpha = aniData.alpha - alphastep;
    end
    aniData.posY = aniData.posY + aniData.addY;
end

----------------------
--Do Rainbow Animation
function SCT:RainbowAnimation(aniData)
    if (aniData.addY > 0) then
            aniData.addY = aniData.addY - 0.22
    else
            aniData.addY = aniData.addY - (0.18 * (self.db.profile["MOVEMENT"]/2));
    end
    if aniData.addY < -7 then aniData.addY = -7 end ;
    aniData.posY = aniData.posY + aniData.addY;
    aniData.posX = aniData.posX - 2.2 * aniData.sidedir;
    if ( aniData.posY < (aniData.bottompoint - sct_MAX_DISTANCE) ) then
        aniData.alpha = aniData.alpha - 0.05;
    end
end

----------------------
--Do Horizontal Animation
function SCT:HorizontalAnimation(aniData)
    local step = math.abs(aniData.addX);
    local alphastep = 0.01 * step;
    local max = sct_SIDE_POINT*.5
    aniData.delay = aniData.delay + 1;
    if (aniData.delay > (max/step)) then
      aniData.alpha = aniData.alpha - alphastep;
    end
    aniData.posX = aniData.posX - (aniData.addX * aniData.sidedir);
end

----------------------
--Do Angled Down Animation
function SCT:AngledDownAnimation(aniData)
    if (aniData.delay <= 13) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY - aniData.addY;
            aniData.posX = aniData.posX - aniData.addX * aniData.sidedir;
    elseif (aniData.delay <= 35) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY + (random(0,70) - 35) * 0.02;
            aniData.posX = aniData.posX + (random(0,70) - 35) * 0.02;
    elseif (aniData.delay <= 50) then
            aniData.delay = aniData.delay + 1;
    else
            aniData.posY = aniData.posY + self.db.profile["MOVEMENT"];
            aniData.posX = aniData.posX - self.db.profile["MOVEMENT"] * aniData.sidedir;
            aniData.alpha = aniData.alpha - 0.02;
    end
end

----------------------
--Do Angled Up Animation
function SCT:AngledUpAnimation(aniData)
    if (aniData.delay <= 13) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY + aniData.addY;
            aniData.posX = aniData.posX - aniData.addX * aniData.sidedir;
    elseif (aniData.delay <= 35) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY + (random(0,70) - 35) * 0.02;
            aniData.posX = aniData.posX + (random(0,70) - 35) * 0.02;
    elseif (aniData.delay <= 50) then
            aniData.delay = aniData.delay + 1;
    else
            aniData.posY = aniData.posY + self.db.profile["MOVEMENT"];
            aniData.alpha = aniData.alpha - 0.02;
    end
end

----------------------
--Do Sprinkler Animation
function SCT:SprinklerAnimation(aniData)
    if (aniData.delay <= (self.db.profile["MOVEMENT"] + 10)) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY + aniData.addY;
            aniData.posX = aniData.posX + aniData.addX;
    elseif (aniData.delay <= 35) then
            aniData.delay = aniData.delay + 1;
            aniData.posY = aniData.posY + (random(0,70) - 35) * 0.02;
            aniData.posX = aniData.posX + (random(0,70) - 35) * 0.02;
    elseif (aniData.delay <= 55) then
            aniData.delay = aniData.delay + 1;
    else
            aniData.posY = aniData.posY + (aniData.addY * .1);
            aniData.posX = aniData.posX + (aniData.addX * .1);
            aniData.alpha = aniData.alpha - 0.02;
    end
end

----------------------
--count the number of crits active
function SCT:CritCount(frame)
    local count = 0;
    local key, value;
    for key, value in pairs(self.ArrayAniData[frame]) do
        if (value.crit) and (value.source == "sct") then
            count = count + 1;
        end
    end
    return count;
end

----------------------
--get the min current min point
function SCT:MinPoint(frame)
    local posY, key, value;
    if (not self.db.profile[self.FRAMES_DATA_TABLE][frame]["DIRECTION"]) then
        posY = self.db.profile[self.FRAMES_DATA_TABLE][frame]["YOFFSET"] + sct_MAX_DISTANCE;
        for key, value in pairs(self.ArrayAniData[frame]) do
            if ((value:IsShown()) and (value.posY < posY) and (value.anitype == 1) and (not value.crit)) then
                posY = value.posY;
            end
        end
    else
        posY = self.db.profile[self.FRAMES_DATA_TABLE][frame]["YOFFSET"];
        for key, value in pairs(self.ArrayAniData[frame]) do
            if ((value:IsShown()) and (value.posY > posY) and (value.anitype == 1) and (not value.crit)) then
                posY = value.posY;
            end
        end
    end
    return posY;
end

-------------------------
--gets the next available animation object
--can be used by SCT addons since public
function SCT:GetNextAniObj(frame)
    local adat, i;
    local anyAvail = false;
    --get first now shown
    for i=1, sct_TEXTCOUNT do
        adat = getglobal("SCTaniData"..i);
        if ( not adat:IsShown() ) then
            anyAvail = true;
            break;
        end
    end
    --if none availble, get oldest
    if (not anyAvail) then
        for i = 1, table.getn(self.ArrayAniData) do
            adat = self.ArrayAniData[i][1];
            if (adat) then break end;
        end
        self:AniReset(adat);
    end
    --set defaults based on frame
    adat.frame = frame;
    adat.posY = self.db.profile[self.FRAMES_DATA_TABLE][frame]["YOFFSET"];
    adat.posX = self.db.profile[self.FRAMES_DATA_TABLE][frame]["XOFFSET"];
    adat.bottompoint = adat.posY;
    adat.toppoint = adat.posY + sct_MAX_DISTANCE;
    adat.font = self.db.profile[self.FRAMES_DATA_TABLE][frame]["FONT"];
    adat.fontshadow = self.db.profile[self.FRAMES_DATA_TABLE][frame]["FONTSHADOW"];
    adat.textsize = self.db.profile[self.FRAMES_DATA_TABLE][frame]["TEXTSIZE"];
    adat.alpha = self.db.profile[self.FRAMES_DATA_TABLE][frame]["ALPHA"]/100;
    adat.anitype = self.db.profile[self.FRAMES_DATA_TABLE][frame]["ANITYPE"];
    adat.anisidetype = self.db.profile[self.FRAMES_DATA_TABLE][frame]["ANISIDETYPE"];
    adat.direction = self.db.profile[self.FRAMES_DATA_TABLE][frame]["DIRECTION"];
    adat.sidedir = self.db.profile[self.FRAMES_DATA_TABLE][frame]["ANISIDETYPE"];
    return adat
end

----------------------
--Rest the text animation
function SCT:AniReset(aniData)
    local i, key, value
    --remove it from display table
    for i = 1, table.getn(self.ArrayAniData) do
        for key, value in pairs(self.ArrayAniData[i]) do
            if ( value == aniData ) then
                tremove(self.ArrayAniData[i], key);
                break;
            end
        end
    end
    --reset all setings
    aniData.crit = false;
    aniData.critsize = nil;
    aniData.posY = 0;
    aniData.posX = 0;
    aniData.addY = 0;
    aniData.addX = 0;
    aniData.alpha = 0;
    aniData.lastupdate = 0;
    aniData.delay = 0;
    aniData.source = "sct";
    aniData:SetAlpha(aniData.alpha);
    aniData:Hide();
    aniData:SetPoint("CENTER", "UIParent", "CENTER", aniData.posX, aniData.posY);
end

----------------------
--Rest all the text animations
function SCT:AniResetAll()
    for i=1, sct_TEXTCOUNT do
        local aniData = getglobal("SCTaniData"..i);
        self:AniReset(aniData);
    end
end

------------------------
--Initial animation settings
function SCT:AniInit()
    self:AniResetAll();
    self:SetMsgFont(SCT_MSG_FRAME);
    SCT_MSG_FRAME:SetPoint("CENTER", "UIParent", "CENTER",
                                                 self.db.profile[self.FRAMES_DATA_TABLE][SCT.MSG]["MSGXOFFSET"],
                                                 self.db.profile[self.FRAMES_DATA_TABLE][SCT.MSG]["MSGYOFFSET"]);
    SCT_MSG_FRAME:SetTimeVisible(self.db.profile[self.FRAMES_DATA_TABLE][SCT.MSG]["MSGFADE"]);
    self:SetDmgFont();
end